using System;
using System.Collections.Generic;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.StateMachine.SPI;

namespace T.FAS.Runtime.StateMachine.Engine
{
    public class RuleController
    {
        public delegate bool RuleProcessEventHandler(TRecord record, TState state, TRule rule, StateMachineMetadata metadata, StringBuilder failReason);

        private static RuleController current;
        private static object lckObj = new object();

        private RuleController()
        {
            Load();
        }

        public static RuleController Current
        {
            get
            {
                if (current == null)
                {
                    lock (lckObj)
                    {
                        if (current == null)
                            current = new RuleController();
                    }
                }
                return current;
            }
        }
        private Dictionary<string, RuleProcessEventHandler> handlers;
        private List<IRuleProcess> sysDefault = new List<IRuleProcess>();

        private void Load()
        {
            handlers = new Dictionary<string, RuleProcessEventHandler>();
            var FrozenRuleProcess = new FrozenRuleProcess();
            handlers.Add(FrozenRuleProcess.Key, new RuleProcessEventHandler(FrozenRuleProcess.Check));
            sysDefault.Add(FrozenRuleProcess);

            var ReadOnlyRuleProcess = new ReadOnlyRuleProcess();
            handlers.Add(ReadOnlyRuleProcess.Key, new RuleProcessEventHandler(ReadOnlyRuleProcess.Check));
            sysDefault.Add(ReadOnlyRuleProcess);

            var MatchRuleProcess = new MatchRuleProcess();
            handlers.Add(MatchRuleProcess.Key, new RuleProcessEventHandler(MatchRuleProcess.Check));
            sysDefault.Add(MatchRuleProcess);

            var NotSameAsRuleProcess = new NotSameAsRuleProcess();
            handlers.Add(NotSameAsRuleProcess.Key, new RuleProcessEventHandler(NotSameAsRuleProcess.Check));
            sysDefault.Add(NotSameAsRuleProcess);

            var RequiredRuleProcess = new RequiredRuleProcess();
            handlers.Add(RequiredRuleProcess.Key, new RuleProcessEventHandler(RequiredRuleProcess.Check));
            sysDefault.Add(RequiredRuleProcess);

            var ExpressRuleProcess = new ExpressionRuleProcess();
            handlers.Add(ExpressRuleProcess.Key, new RuleProcessEventHandler(ExpressRuleProcess.Check));

        }

        /// <summary>
        /// 校验规则
        /// </summary>
        /// <param name="record"></param>
        /// <param name="state"></param>
        /// <param name="metadata"></param>
        /// <returns></returns>
        public ValidResult Check(TRecord record, TState state, StateMachineMetadata metadata)
        {
            ValidResult result = new ValidResult();
            var errorPrefix = string.Format(CommonStringClass.RuleController_Check_Prefix, metadata?.Code);
            if (record == null || state == null || metadata == null)
            {
                result.FailReason = CommonStringClass.RuleController_Check2;
                result.IsSuccess = false;
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                result.IsSuccess = true; 
                foreach (var rule in state.Rules)
                {
                    //有前置条件，且前置条件表达式结果为False时，不执行规则校验
                    if (IsNeed(rule, record, state, metadata) == false)
                        continue;
                    foreach (var ruleContent in rule.RuleContents)
                    {
                        if (string.IsNullOrEmpty(ruleContent?.RuleInfo))
                            throw new FasException(SMErrorCode.Error, string.Format(CommonStringClass.RuleController_Check,errorPrefix,state.Name,rule.ID));
                        if (ruleContent.RuleType == RuleType.System)
                        {
                            if (handlers.ContainsKey(ruleContent.RuleInfo) == false)
                            {
                                return result;  //暂时先返回
                                throw new Exception(CommonStringClass.RuleController_Check1+ ruleContent.RuleInfo);
                            }
                            var tResult = handlers[ruleContent.RuleInfo].Invoke(record, state, rule, metadata, sb);
                            if (tResult == false)
                            {
                                result.IsSuccess = false;
                                result.FailReason = sb.ToString();
                            }
                        }
                        else
                        {
                            var tResult = handlers[ExpressionRuleProcess.CSTKEY].Invoke(record, state, rule, metadata, sb);
                            if (tResult == false)
                            {
                                result.IsSuccess = false;
                                result.FailReason = sb.ToString();
                            }
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 是否需要前置规则校验
        /// </summary>
        /// <param name="rule">规则 rule.HasCondition前置条件 false:无前置条件，true:有前置条件</param>
        /// <param name="record">Record</param>
        /// <returns></returns>
        private bool IsNeed(TRule rule, TRecord record, TState state, StateMachineMetadata sm)
        {
            if (rule.HasCondition == false)
                return true;

            Dictionary<string, object> expParam = new Dictionary<string, object>();
            expParam.Add("record", record);

            var fasContext = new FASContext
            {
                CurrentRecord = record,
                PKColumnName = record.PKColumnName,
            };

            var i18NDecription = I18NUtil.GetTranslatedText(rule.GetRefSchemaExpr(sm), rule.Description);
            var errorPrefix = string.Format(CommonStringClass.RuleController_IsNeed_Prefix, sm?.Code, state?.ID, rule.Name, i18NDecription);
            var result = Expression.Service.ExprService.GetInstance.Execute(rule.ConditionExpressionJson, fasContext, expParam);
            if (result.State == Expression.Entity.ResultState.Fail)
                throw new Exception(string.Format(CommonStringClass.RuleController_IsNeed, errorPrefix) + result.ErrMsg);

            return Convert.ToBoolean(result.Result);
        }

        /// <summary>
        /// 获取系统预制的规则
        /// </summary>
        /// <returns></returns>
        public List<IRuleProcess> GetSysDefaultRules()
        {
            return sysDefault;
        }
    }
}
